﻿using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Json;
using Abp.Logging;
using Abp.Runtime.Caching;
using Abp.Runtime.Session;
using Abp.Threading;
using Castle.Core.Logging;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Core.WorkFlows.Design.Cache;
using Mt.Site.Core.WorkFlows.Design.DiyForm;
using Mt.Site.Core.WorkFlows.Design.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Mt.Site.Core.WorkFlows.Design
{
    public class DefaultWorkFlowFactory : IWorkFlowFactory, ITransientDependency
    {
        public readonly char[] SplitChars = WorkFlowConst.SplitChars.ToCharArray();

        private readonly ICacheManager _cacheManager;

        private readonly IRepository<WorkFlow, Guid> _workFlowRepository;
        private readonly IRepository<WorkFlowNode, Guid> _workFlowNodeRepository;
        private readonly IRepository<WorkFlowForm, Guid> _workFlowFormRepository;
        private readonly IRepository<WorkFlowPaperListCtrl, Guid> _workFlowPaperListCtrlRepository;

        public IAbpSession AbpSession { get; set; }
        public ILogger Logger { get; set; }

        public DefaultWorkFlowFactory(
            ICacheManager cacheManager,
            IRepository<WorkFlow, Guid> workFlowRepository,
            IRepository<WorkFlowNode, Guid> workFlowNodeRepository,
            IRepository<WorkFlowForm, Guid> workFlowFormRepository,
            IRepository<WorkFlowPaperListCtrl, Guid> workFlowPaperListCtrlRepository)
        {
            _cacheManager = cacheManager;
            _workFlowRepository = workFlowRepository;
            _workFlowNodeRepository = workFlowNodeRepository;
            _workFlowFormRepository = workFlowFormRepository;
            _workFlowPaperListCtrlRepository = workFlowPaperListCtrlRepository;

            AbpSession = NullAbpSession.Instance;
            Logger = NullLogger.Instance;
        }

        public async Task<IWorkflowDefinition> GetWorkflowDefinitionAsync(Guid workFlowId)
        {
            var workFlow = await GetWorkFlowAsync(workFlowId);
            if(workFlow == null)
            {
                throw new ArgumentException($"该流程不存在,workFlowId:{workFlowId}");
            }

            var workflowDefinition = new DefaultWorkflowDefinition
            {
                Definition = workFlow
            };
            return workflowDefinition;
        }

        public async Task<List<IWorkflowDefinition>> GetAllWorkFlowDefinitionAsync()
        {
            var query = from wf in _workFlowRepository.GetAll()
                        where wf.IsActive
                        select wf.Id;

            var workFlowIds = await query.ToListAsync();

            var result = new List<IWorkflowDefinition>();
            foreach (var id in workFlowIds)
            {
                var wf = await GetWorkflowDefinitionAsync(id);
                result.Add(wf);
            }
            return result;
        }

        private async Task<WorkFlowCacheItem> GetWorkFlowAsync(Guid workFlowId)
        {
            var cacheKey = $"{workFlowId}@{AbpSession.GetTenantId()}";
            return await _cacheManager.GetWorkFlowCache().GetAsync(cacheKey, async () => await GetCacheItemAsync(workFlowId));
        }

        private async Task<WorkFlowCacheItem> GetCacheItemAsync(Guid workFlowId)
        {
            var workFlow = await _workFlowRepository.FirstOrDefaultAsync(c => c.Id == workFlowId);
            if (workFlow == null)
            {
                return null;
            }

            var newCacheItem = new WorkFlowCacheItem
            {
                WorkFlowId = workFlow.Id,
                Order = workFlow.Order,
                Name = workFlow.Name,
                ShortName = workFlow.ShortName,
                IsActive = workFlow.IsActive,
                CategeoryId = workFlow.CategeoryId,
                IsHaveAttachment = workFlow.IsHaveAttachment,
                IsHaveOnlineWord = workFlow.IsHaveOnlineWord,
                EntrustType = workFlow.EntrustType,
                Description = workFlow.Description,
                NumberFormat = workFlow.NumberFormat,
                NumberCounting = workFlow.NumberCounting,
                DigitNumber = workFlow.DigitNumber,
                ShouldLogWhenDrawback = workFlow.ShouldLogWhenDrawback ,
                OptionFunction = workFlow.OptionFunction,
                CustomData = (workFlow.CustomData.IsNullOrWhiteSpace()? null: workFlow.CustomData.FromJsonString<WorkflowCustomData>() )
            };

            newCacheItem.UnitIdList = GetLongArryFromStr(workFlow.UnitIdList);

            //拉取步骤定义
            var workFlowNodeList = await _workFlowNodeRepository.GetAllListAsync(c => c.WorkflowId == workFlowId); // && c.IsActive
            newCacheItem.NodeList = workFlowNodeList
                .OrderBy(c => c.Order)
                .Select(c =>
            {
                var nodeItem = new WorkFlowNodeCacheItem
                {
                    WorkflowId = c.WorkflowId,
                    NodeId = c.Id,
                    Order = c.Order,
                    Name = c.Name,
                    ShortName = c.ShortName,
                    Description = c.Description,
                    ActionScheme = c.ActionScheme,
                    CanEditAttachment = c.CanEditAttachment,
                    BeforeSelectFilter = c.BeforeSelectFilter,
                    SelectedFilter = c.SelectedFilter,
                    HandlingTimeLimit = c.HandlingTimeLimit,
                    IsAutoSendToNext = c.IsAutoSendToNext,
                    OptionFunction = c.OptionFunction,
                    CustomData = (workFlow.CustomData.IsNullOrWhiteSpace() ? null : workFlow.CustomData.FromJsonString<WorkflowNodeCustomData>()) ,
                    IsActive = c.IsActive
                };
                nodeItem.NextNodeList = GetGuidArryFromStr(c.NextNodeList, workFlowNodeList, c.IsActive);
                nodeItem.BackNodeList = GetGuidArryFromStr(c.BackNodeList, workFlowNodeList, c.IsActive);
                nodeItem.UserIdList = GetLongArryFromStr(c.UserIdList);
                nodeItem.ActionList = GetStringArryFromStr(c.ActionList);
                nodeItem.BeforeSelectMoreFilter = GetStringArryFromStr(c.BeforeSelectMoreFilter);
                nodeItem.SelectedMoreFilter = GetStringArryFromStr(c.SelectedMoreFilter);
                return nodeItem;
            }).ToArray();

            //拉取表单定义
            var formEntity = await _workFlowFormRepository.FirstOrDefaultAsync(c => c.WorkFlowId == workFlowId && c.IsActive);
            if (formEntity != null)
            {
                newCacheItem.FormId = formEntity.Id;
                newCacheItem.FormHtml = formEntity.HtmlData;
                try
                {
                    newCacheItem.FormConfig = formEntity.ExtensionData.FromJsonString<List<FormConfigItem>>().ToArray();
                    newCacheItem.FormListCtrlConfig = new Dictionary<string, FormConfigItem[]>();
                    foreach (var item in newCacheItem.FormConfig)
                    {
                        if (item.FieldType == FieldType.ListCtrl)
                        {
                            if (Guid.TryParse(item.CustomData, out Guid listCtrlId))
                            {
                                var listCtrlEntity = await _workFlowPaperListCtrlRepository.FirstOrDefaultAsync(c => c.Id == listCtrlId && c.IsActive);
                                if (listCtrlEntity != null)
                                {
                                    var listCtrlConfig = listCtrlEntity.ExtensionData.FromJsonString<List<FormConfigItem>>().ToArray();
                                    newCacheItem.FormListCtrlConfig.Add(listCtrlEntity.Id.ToString(), listCtrlConfig);
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.LogException(Logger,ex);
                    newCacheItem.FormConfig = new FormConfigItem[] { };
                }
            }
            else
            {
                newCacheItem.FormConfig = new FormConfigItem[] { };
            }

            return newCacheItem;
        }

        private long[] GetLongArryFromStr(string listStr)
        {
            long[] unitIdList;
            if (!listStr.IsNullOrEmpty())
            {
                unitIdList = listStr.Split(SplitChars, StringSplitOptions.RemoveEmptyEntries).Select(c => long.Parse(c)).ToArray();
            }
            else
            {
                unitIdList = new long[] { };
            }

            return unitIdList;
        }
        private Guid[] GetGuidArryFromStr(string listStr, List<WorkFlowNode> workFlowNodeList, bool isActive)
        {
            Guid[] nodeIdList;
            if (!listStr.IsNullOrEmpty())
            {
                nodeIdList = listStr.Split(SplitChars, StringSplitOptions.RemoveEmptyEntries)
                    .Where(c => {
                        var nodeItem = workFlowNodeList.FirstOrDefault(d => d.Id.ToString().ToLower() == c);
                        if (nodeItem != null)
                        {
                            if (isActive)
                            {
                                return nodeItem.IsActive;
                            }
                            //对于已撤销发布的步骤，需要保留，确保之前的稿件可以正常打开
                            return true;
                        }
                        return false;
                    })
                    .Select(c => Guid.Parse(c))
                    .ToArray();
            }
            else
            {
                nodeIdList = new Guid[] { };
            }

            return nodeIdList;
        }
        private string[] GetStringArryFromStr(string listStr)
        {
            string[] unitIdList;
            if (!listStr.IsNullOrEmpty())
            {
                unitIdList = listStr.Split(SplitChars, StringSplitOptions.RemoveEmptyEntries).ToArray();
            }
            else
            {
                unitIdList = new string[] { };
            }

            return unitIdList;
        }
    }
}
